#include "../hle.h"

#include "__sceAudio.h"

#define SAMPLE_MIN 64
#define SAMPLE_MAX 65472

AudioChannel chans[MAX_CHANNEL];

void sceAudioOutputPannedBlocking()
{
    s32 chanNumber = PARAM(0);
    u32 leftVol = PARAM(1);
    u32 rightVol = PARAM(2);
    u32 samplePtr = PARAM(3);

    if (samplePtr == 0)
    {
        _log(ERR, HLE, "sceAudioOutputPannedBlocking(channel = %d, leftvol = %d, rightvol = %d, buf = %08x): waiting for playing sounds", chanNumber, leftVol, rightVol, samplePtr);
        RETURN(0);
        return;
    }

    if (chanNumber < 0 || chanNumber > MAX_CHANNEL) {
        _log(ERR, HLE, "sceAudioOutputPannedBlocking(channel = %d, leftvol = %d, rightvol = %d, buf = %08x): invalid channel", chanNumber, leftVol, rightVol, samplePtr);
        RETURN(SCE_ERROR_AUDIO_INVALID_CHANNEL);
    }
    else if (!chans[chanNumber].reserved) {
        _log(ERR, HLE, "sceAudioOutputPannedBlocking(channel = %d, leftvol = %d, rightvol = %d, buf = %08x): channel not reserved", chanNumber, leftVol, rightVol, samplePtr);
        RETURN(SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED);
    }
    else
    {
        _log(DBG, HLE, "sceAudioOutputPannedBlocking(channel = %d, leftvol = %d, rightvol = %d, buf = %08x)", chanNumber, PARAM(1), PARAM(2), samplePtr);
        chans[chanNumber].leftVol    = leftVol;
        chans[chanNumber].rightVol   = rightVol;
        chans[chanNumber].sampleAddr = samplePtr;
        __AudioPlay(&chans[chanNumber], chanNumber);
        RETURN(0);
    }
}

void sceAudioOutputBlocking()
{
    s32 chanNumber = PARAM(0);
    u32 vol = PARAM(1);
    u32 samplePtr = PARAM(2);

    if (chanNumber < 0 || chanNumber > MAX_CHANNEL) {
        _log(ERR, HLE, "sceAudioOutputBlocking(channel = %d, vol = %d, buf = %08x): invalid channel", chanNumber, vol, samplePtr);
        RETURN(SCE_ERROR_AUDIO_INVALID_CHANNEL);
    }
    else if (!chans[chanNumber].reserved) {
        _log(ERR, HLE, "sceAudioOutputBlocking(channel = %d, vol = %d, buf = %08x): channel not reserved", chanNumber, vol, samplePtr);
        RETURN(SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED);
    }
    else
    {
        _log(DBG, HLE, "sceAudioOutputBlocking(channel = %d, vol = %d, buf = %08x)", chanNumber, vol, samplePtr);
        chans[chanNumber].leftVol    = vol;
        chans[chanNumber].rightVol   = vol;
        chans[chanNumber].sampleAddr = samplePtr;
        __AudioPlay(&chans[chanNumber], chanNumber);
        RETURN(0);
    }
}

void sceAudioChReserve()
{
    s32 channel = PARAM(0);
    u32 sampleCount = PARAM(1);
    u32 format = PARAM(2);

    s32 realChannel = -1;

    if (sampleCount >= SAMPLE_MIN && sampleCount <= SAMPLE_MAX)
    {
        if ((sampleCount & 0x3f) != 0)
        {
            _log(ERR, HLE, "sceAudioChReserve(channel = %d, sampleCount = %d, format = %d): sample data size not aligned", channel, sampleCount, format);
            RETURN(SCE_ERROR_AUDIO_OUTPUT_SAMPLE_DATA_SIZE_NOT_ALIGNED);
            return;
        }
    }
    else
    {
        _log(ERR, HLE, "sceAudioChReserve(channel = %d, sampleCount = %d, format = %d): invalid sample count", channel, sampleCount, format);
        RETURN(-1);
        return;
    }

    if (format != AUDIO_FORMAT_MONO && format != AUDIO_FORMAT_STEREO)
    {
        _log(ERR, HLE, "sceAudioChReserve(channel = %d, sampleCount = %d, format = %d): invalid format", channel, sampleCount, format);
        RETURN(SCE_ERROR_AUDIO_INVALID_FORMAT);
        return;
    }

    /* Choose the first available channel */
    if (channel == -1)
    {
        u8 i;
        for (i = 0; i < MAX_CHANNEL; i++)
        {
            if (!chans[i].reserved) {
                realChannel = i;
                break;
            }
        }
        if (realChannel == -1) /* Didn't find any */
        {
            _log(ERR, HLE, "sceAudioChReserve(channel = %d, sampleCount = %d, format = %d): no channels available", channel, sampleCount, format);
            RETURN(SCE_ERROR_AUDIO_NO_CHANNELS_AVAILABLE);
            return;
        }
    }
    else
    {
        if (channel > 0 && channel < MAX_CHANNEL)
        {
            if (!chans[channel].reserved)
                realChannel = channel;
            else
            {
                _log(ERR, HLE, "sceAudioChReserve(channel = %d, sampleCount = %d, format = %d): channel busy", channel, sampleCount, format);
                RETURN(SCE_ERROR_AUDIO_CHANNEL_BUSY);
                return;
            }
        }
        else
        {
            _log(ERR, HLE, "sceAudioChReserve(channel = %d, sampleCount = %d, format = %d): invalid channel", channel, sampleCount, format);
            RETURN(SCE_ERROR_AUDIO_INVALID_CHANNEL);
            return;
        }
    }
    
    chans[realChannel].sampleCount = sampleCount;
    chans[realChannel].format      = format;
    chans[realChannel].reserved    = 1;
    chans[realChannel].running     = 0;
    _log(INF, HLE, "%d = sceAudioChReserve(channel = %d, sampleCount = %d, format = %d)", realChannel, channel, sampleCount, format);

    RETURN(realChannel);
}

void sceAudioChRelease()
{
    _log(ERR, HLE, "sceAudioChRelease");
    RETURN(1);
}

void sceAudioSetChannelDataLen()
{
    u32 channel = PARAM(0);
    u32 sampleCount = PARAM(1);

    if (sampleCount >= SAMPLE_MIN && sampleCount <= SAMPLE_MAX)
    {
        if ((sampleCount & 0x3f) != 0)
        {
            _log(ERR, HLE, "sceAudioSetChannelDataLen(channel = %d, sampleCount = %08x): sample count not aligned", channel, sampleCount);
            RETURN(SCE_ERROR_AUDIO_OUTPUT_SAMPLE_DATA_SIZE_NOT_ALIGNED);
            return;
        }
    }
    else
    {
        _log(ERR, HLE, "sceAudioSetChannelDataLen(channel = %d, sampleCount = %08x): invalid sample count", channel, sampleCount);
        RETURN(-1);
        return;
    }

    if (channel > 0 && channel < MAX_CHANNEL)
    {
        if (!chans[channel].reserved)
        {
            _log(ERR, HLE, "sceAudioSetChannelDataLen(channel = %d, sampleCount = %08x): channel not reserved", channel, sampleCount);
            RETURN(SCE_ERROR_AUDIO_CHANNEL_NOT_INIT);
            return;
        }
    }
    else
    {
        _log(ERR, HLE, "sceAudioSetChannelDataLen(channel = %d, sampleCount = %08x): invalid channel", channel, sampleCount);
        RETURN(SCE_ERROR_AUDIO_INVALID_CHANNEL);
        return;
    }

    chans[channel].sampleCount = sampleCount;
    _log(INF, HLE, "0 = sceAudioSetChannelDataLen(channel = %d, sampleCount = %08x)", channel, sampleCount);

    RETURN(0);
}

void sceAudioChangeChannelConfig()
{
    u32 channel = PARAM(0);
    u32 format = PARAM(1);

    if (format != AUDIO_FORMAT_MONO && format != AUDIO_FORMAT_STEREO)
    {
        _log(ERR, HLE, "sceAudioChangeChannelConfig(channel = %d, format = %08x): invalid format", channel, format);
        RETURN(SCE_ERROR_AUDIO_INVALID_FORMAT);
        return;
    }

    if (channel > 0 && channel < MAX_CHANNEL)
    {
        if (!chans[channel].reserved)
        {
            _log(ERR, HLE, "sceAudioChangeChannelConfig(channel = %d, format = %08x): channel not reserved", channel, format);
            RETURN(SCE_ERROR_AUDIO_CHANNEL_NOT_INIT);
            return;
        }
    }
    else
    {
        _log(ERR, HLE, "sceAudioChangeChannelConfig(channel = %d, format = %08x): invalid channel", channel, format);
        RETURN(SCE_ERROR_AUDIO_INVALID_CHANNEL);
        return;
    }

    chans[channel].format = format;
    _log(INF, HLE, "0 = sceAudioChangeChannelConfig(channel = %d, format = %08x)", channel, format);

    RETURN(0);
}

void sceAudioGetChannelRestLen()
{
    _log(ERR, HLE, "sceAudioGetChannelRestLen(%d)", PARAM(0));
    RETURN(0);
}

